
#include "xml_conv.hpp"

#include <cstring>
#include <algorithm>


#if _MSC_VER
#define snprintf _snprintf
#endif

namespace mp {


	namespace xml
	{
		struct named_color 
		{
			std::string name;
			xml_color color;
		};

		static const int named_color_count = 147;
		static const named_color named_colors[named_color_count] =
		{            
			{"aliceblue", xml_color(240, 248, 255)},
			{"antiquewhite", xml_color(250, 235, 215)},
			{"aqua", xml_color(0, 255, 255)},
			{"aquamarine", xml_color(127, 255, 212)},
			{"azure", xml_color(240, 255, 255)},
			{"beige", xml_color(245, 245, 220)},
			{"bisque", xml_color(255, 228, 196)},
			{"black", xml_color(0, 0, 0)},
			{"blanchedalmond", xml_color(255,235,205)},
			{"blue", xml_color(0, 0, 255)},
			{"blueviolet", xml_color(138, 43, 226)},
			{"brown", xml_color(165, 42, 42)},
			{"burlywood", xml_color(222, 184, 135)},
			{"cadetblue", xml_color(95, 158, 160)},
			{"chartreuse", xml_color(127, 255, 0)},
			{"chocolate", xml_color(210, 105, 30)},
			{"coral", xml_color(255, 127, 80)},
			{"cornflowerblue", xml_color(100, 149, 237)},
			{"cornsilk", xml_color(255, 248, 220)},
			{"crimson", xml_color(220, 20, 60)},
			{"cyan", xml_color(0, 255, 255)},
			{"darkblue", xml_color(0, 0, 139)},
			{"darkcyan", xml_color(0, 139, 139)},
			{"darkgoldenrod", xml_color(184, 134, 11)},
			{"darkgray", xml_color(169, 169, 169)},
			{"darkgreen", xml_color(0, 100, 0)},
			{"darkgrey", xml_color(169, 169, 169)},
			{"darkkhaki", xml_color(189, 183, 107)},
			{"darkmagenta", xml_color(139, 0, 139)},
			{"darkolivegreen", xml_color(85, 107, 47)},
			{"darkorange", xml_color(255, 140, 0)},
			{"darkorchid", xml_color(153, 50, 204)},
			{"darkred", xml_color(139, 0, 0)},
			{"darksalmon", xml_color(233, 150, 122)},
			{"darkseagreen", xml_color(143, 188, 143)},
			{"darkslateblue", xml_color(72, 61, 139)},
			{"darkslategrey", xml_color(47, 79, 79)},
			{"darkturquoise", xml_color(0, 206, 209)},
			{"darkviolet", xml_color(148, 0, 211)},
			{"deeppink", xml_color(255, 20, 147)},
			{"deepskyblue", xml_color(0, 191, 255)},
			{"dimgray", xml_color(105, 105, 105)},
			{"dimgrey", xml_color(105, 105, 105)},
			{"dodgerblue", xml_color(30, 144, 255)},
			{"firebrick", xml_color(178, 34, 34)},
			{"floralwhite", xml_color(255, 250, 240)},
			{"forestgreen", xml_color(34, 139, 34)},
			{"fuchsia", xml_color(255, 0, 255)},
			{"gainsboro", xml_color(220, 220, 220)},
			{"ghostwhite", xml_color(248, 248, 255)},
			{"gold", xml_color(255, 215, 0)},
			{"goldenrod", xml_color(218, 165, 32)},
			{"gray", xml_color(128, 128, 128)},
			{"grey", xml_color(128, 128, 128)},
			{"green", xml_color(0, 128, 0)},
			{"greenyellow", xml_color(173, 255, 47)},
			{"honeydew", xml_color(240, 255, 240)},
			{"hotpink", xml_color(255, 105, 180)},
			{"indianred", xml_color(205, 92, 92)},
			{"indigo", xml_color(75, 0, 130)},
			{"ivory", xml_color(255, 255, 240)},
			{"khaki", xml_color(240, 230, 140)},
			{"lavender", xml_color(230, 230, 250)},
			{"lavenderblush", xml_color(255, 240, 245)},
			{"lawngreen", xml_color(124, 252, 0)},
			{"lemonchiffon", xml_color(255, 250, 205)},
			{"lightblue", xml_color(173, 216, 230)},
			{"lightcoral", xml_color(240, 128, 128)},
			{"lightcyan", xml_color(224, 255, 255)},
			{"lightgoldenrodyellow", xml_color(250, 250, 210)},
			{"lightgray", xml_color(211, 211, 211)},
			{"lightgreen", xml_color(144, 238, 144)},
			{"lightgrey", xml_color(211, 211, 211)},
			{"lightpink", xml_color(255, 182, 193)},
			{"lightsalmon", xml_color(255, 160, 122)},
			{"lightseagreen", xml_color(32, 178, 170)},
			{"lightskyblue", xml_color(135, 206, 250)},
			{"lightslategray", xml_color(119, 136, 153)},
			{"lightslategrey", xml_color(119, 136, 153)},
			{"lightsteelblue", xml_color(176, 196, 222)},
			{"lightyellow", xml_color(255, 255, 224)},
			{"lime", xml_color(0, 255, 0)},
			{"limegreen", xml_color(50, 205, 50)},
			{"linen", xml_color(250, 240, 230)},
			{"magenta", xml_color(255, 0, 255)},
			{"maroon", xml_color(128, 0, 0)},
			{"mediumaquamarine", xml_color(102, 205, 170)},
			{"mediumblue", xml_color(0, 0, 205)},
			{"mediumorchid", xml_color(186, 85, 211)},
			{"mediumpurple", xml_color(147, 112, 219)},
			{"mediumseagreen", xml_color(60, 179, 113)},
			{"mediumslateblue", xml_color(123, 104, 238)},
			{"mediumspringgreen", xml_color(0, 250, 154)},
			{"mediumturquoise", xml_color(72, 209, 204)},
			{"mediumvioletred", xml_color(199, 21, 133)},
			{"midnightblue", xml_color(25, 25, 112)},
			{"mintcream", xml_color(245, 255, 250)},
			{"mistyrose", xml_color(255, 228, 225)},
			{"moccasin", xml_color(255, 228, 181)},
			{"navajowhite", xml_color(255, 222, 173)},
			{"navy", xml_color(0, 0, 128)},
			{"oldlace", xml_color(253, 245, 230)},
			{"olive", xml_color(128, 128, 0)},
			{"olivedrab", xml_color(107, 142, 35)},
			{"orange", xml_color(255, 165, 0)},
			{"orangered", xml_color(255, 69, 0)},
			{"orchid", xml_color(218, 112, 214)},
			{"palegoldenrod", xml_color(238, 232, 170)},
			{"palegreen", xml_color(152, 251, 152)},
			{"paleturquoise", xml_color(175, 238, 238)},
			{"palevioletred", xml_color(219, 112, 147)},
			{"papayawhip", xml_color(255, 239, 213)},
			{"peachpuff", xml_color(255, 218, 185)},
			{"peru", xml_color(205, 133, 63)},
			{"pink", xml_color(255, 192, 203)},
			{"plum", xml_color(221, 160, 221)},
			{"powderblue", xml_color(176, 224, 230)},
			{"purple", xml_color(128, 0, 128)},
			{"red", xml_color(255, 0, 0)},
			{"rosybrown", xml_color(188, 143, 143)},
			{"royalblue", xml_color(65, 105, 225)},
			{"saddlebrown", xml_color(139, 69, 19)},
			{"salmon", xml_color(250, 128, 114)},
			{"sandybrown", xml_color(244, 164, 96)},
			{"seagreen", xml_color(46, 139, 87)},
			{"seashell", xml_color(255, 245, 238)},
			{"sienna", xml_color(160, 82, 45)},
			{"silver", xml_color(192, 192, 192)},
			{"skyblue", xml_color(135, 206, 235)},
			{"slateblue", xml_color(106, 90, 205)},
			{"slategray", xml_color(112, 128, 144)},
			{"slategrey", xml_color(112, 128, 144)},
			{"snow", xml_color(255, 250, 250)},
			{"springgreen", xml_color(0, 255, 127)},
			{"steelblue", xml_color(70, 130, 180)},
			{"tan", xml_color(210, 180, 140)},
			{"teal", xml_color(0, 128, 128)},
			{"thistle", xml_color(216, 191, 216)},
			{"tomato", xml_color(255, 99, 71)},
			{"turquoise", xml_color(64, 224, 208)},
			{"violet", xml_color(238, 130, 238)},
			{"wheat", xml_color(245, 222, 179)},
			{"white", xml_color(255, 255, 255)},
			{"whitesmoke", xml_color(245, 245, 245)},
			{"yellow", xml_color(255, 255, 0)},
			{"yellowgreen", xml_color(154, 205, 50)},
			{"transparent", xml_color(0, 0, 0, 0)}
		};

		bool getCssColor(const char* str,xml_color& result)
		{
			for (int i=0;i<named_color_count;i++)
			{
				if (named_colors[i].name == str)
				{
					result = named_colors[i].color;
					return true;
				}
			}
			
			return false;
		}
	}
	namespace xml {

		bool string2bool(std::string const& value, bool & result)
		{
			if (value.empty() || value.size() > 5) {
				return false;
			} else if (value == "true") {
				return result = true;
			} else if (value == "false") {
				result = false;
				return true;
			}
			std::string val(value);
			std::transform(val.begin(), val.end(), val.begin(), ::tolower);
			if (val == "true" || val == "yes" || val == "1" || val == "on") {
				return result = true;
			} else if (val == "false" || val == "no" || val == "0" || val == "off") {
				result = false;
				return true;
			}
			return false;
		}

		bool string2bool(const char * str, bool & result)
		{
			std::string val(str);
			return string2bool(val,result);
		}

		bool string2color(std::string const& value, xml_color & result)
		{
			return string2color(value.c_str(),result);
		}

		bool string2color(const char * str, xml_color & result)
		{
			//! TODO: css_color_conv
			bool getOk = getCssColor(str,result);
			if (!getOk)
				sscanf(str,"%x",&result);
			return true;
		}

		bool string2uint(std::string const& value, unsigned & result)
		{
			return string2uint(value.c_str(),result);
		}

		bool string2uint(const char * str, unsigned & result)
		{
			sscanf(str,"%d",&result);
			return true;
		}

		bool string2int(std::string const& value, int & result)
		{
			return string2int(value.c_str(),result);
		}

		bool string2int(const char * str, int & result)
		{
			sscanf(str,"%d",&result);
			return true;
		}

		bool string2double(std::string const& value, double & result)
		{
			return string2double(value.c_str(),result);
		}

		bool string2double(const char * str, double & result)
		{
			sscanf(str,"%lf",&result);
			return true;
		}

		bool string2float(std::string const& value, float & result)
		{
			return string2float(value.c_str(),result);
		}

		bool string2float(const char * str, float & result)
		{
			sscanf(str,"%f",&result);
			return true;
		}

		// double conversion - here we use sprintf over karma to work
		// around https://github.com/mapnik/mapnik/issues/1741
		bool to_string(std::string & s, double val)
		{
			s.resize(s.capacity());
			while (true)
			{
				size_t n2 = static_cast<size_t>(snprintf(&s[0], s.size()+1, "%g", val));
				if (n2 <= s.size())
				{
					s.resize(n2);
					break;
				}
				s.resize(n2);
			}
			return true;
		}


		bool to_string(std::string & s, int val)
		{
			s.resize(s.capacity());
			while (true)
			{
				size_t n2 = static_cast<size_t>(snprintf(&s[0], s.size()+1, "%d", val));
				if (n2 <= s.size())
				{
					s.resize(n2);
					break;
				}
				s.resize(n2);
			}
			return true;
		}

		bool to_string(std::string & s, unsigned val)
		{
			s.resize(s.capacity());
			while (true)
			{
				size_t n2 = static_cast<size_t>(snprintf(&s[0], s.size()+1, "%u", val));
				if (n2 <= s.size())
				{
					s.resize(n2);
					break;
				}
				s.resize(n2);
			}
			return true;
		}

		bool to_string(std::string & s, bool val)
		{
			if (val) s = "true";
			else s = "false";
			return true;
		}

	} 

}

#ifdef _WINDOWS
#include <string>
#include <vector>
#define NOMINMAX
#include <windows.h>

namespace mp
{
	namespace xml
	{
		std::string utf16_to_utf8(std::wstring const& wstr)
		{
			std::string str;
			int size = WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, 0, 0, 0, 0);
			if(size > 0)
			{
				std::vector<char> buffer(size);
				WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, &buffer[0], size, 0, 0);
				str.assign(buffer.begin(), buffer.end() - 1);
			}
			return str;
		}

		std::wstring utf8_to_utf16 (std::string const& str)
		{
			std::wstring wstr;
			int size = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, 0, 0);
			if (size > 0)
			{
				std::vector<wchar_t> buffer(size);
				MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, &buffer[0], size);
				wstr.assign(buffer.begin(), buffer.end() - 1);
			}
			return wstr;
		}
	}
}

#endif // _WINDOWS